﻿using System;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace WcfMasterSlaveElectionService
{

    internal static class ChannelFactoryHelper
    {
        static ChannelFactory<IMasterElectionService> factory = null;
        static ChannelFactoryHelper()
        {
            NetTcpBinding binding = new NetTcpBinding(SecurityMode.None, false);
            factory = new ChannelFactory<IMasterElectionService>(binding);
        }

        //TODO: need service locater pattern to get logger!
        public static IMasterElectionService CreateServiceManagementChannel(ILog logger, string uri)
        {
            IMasterElectionService channel = null;

            try
            {
                logger.LogMessage("Creating ChannelFactory for URI: " + uri);
                factory.Endpoint.Address = new EndpointAddress(uri);
                //factory = new ChannelFactory<IMasterElectionService>(binding, new EndpointAddress(uri));
                // Add behaviours:factory.Endpoint.Behaviors.Add(new ClientOutputMessageInspector());
                // add factory: factory.Credentials.ClientCertificate.Certificate = cert;
                channel = factory.CreateChannel();
            }
            catch (Exception ex)
            {
                logger.LogMessage(ex, "General exception while creating channel for URI: " + uri);
            }

            return channel;
        }
    }

    internal abstract class ServiceCaller
    {
        #region Fields
        protected readonly ServiceEndpoint _serviceEndpoint;
        #endregion

        protected ServiceCaller(ServiceEndpoint serviceEnpoint)
        {
            _serviceEndpoint = serviceEnpoint;
        }

        protected abstract void PerformOperation(IMasterElectionService client);
        public void Execute()
        {
            IMasterElectionService masterElectionClient = null;
            try
            {
                masterElectionClient = ChannelFactoryHelper.CreateServiceManagementChannel(_serviceEndpoint.Logger, _serviceEndpoint.Uri);

                if (masterElectionClient == null)
                {
                    return;
                }

                using (OperationContextScope scope = new OperationContextScope((IContextChannel)masterElectionClient))
                {
                    using (masterElectionClient as IDisposable)
                    {

                        this.PerformOperation(masterElectionClient);
                    }
                }
            }
            catch (CommunicationException ex)
            {
                _serviceEndpoint.Logger.LogMessage(ex, "Error communicating with server: " + _serviceEndpoint.Uri);

            }
            catch (TimeoutException ex)
            {
                _serviceEndpoint.Logger.LogMessage(ex, "Timeout with server: " + _serviceEndpoint.Uri);
            }
            catch (Exception ex)
            {
                _serviceEndpoint.Logger.LogMessage(ex, "General exception in communication with server: " + _serviceEndpoint.Uri);
            }
        }
    }

    /// <summary>
    /// Class for calling BroadcastVictory on a service, passing the ID of the newly selected master. The Id of the master
    /// is passed in the constructor. The service call has no result object.
    /// </summary>
    internal class BroadcastVictoryServiceCaller : ServiceCaller
    {
        /// <summary>
        /// Contains the ID of the newly selected master
        /// </summary>
        private string newMaster = null;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="logger"></param>
        /// <param name="NewMasterId"></param>
        public BroadcastVictoryServiceCaller(ServiceEndpoint serviceEnpoint, string newMasterId)
            : base(serviceEnpoint)
        {
            newMaster = newMasterId;
        }

        /// <summary>
        /// Called by the Execute method in the base class.
        /// </summary>
        /// <param name="serviceClient"></param>
        protected override void PerformOperation(IMasterElectionService serviceClient)
        {
            _serviceEndpoint.Logger.LogMessage("Calling BroadCastVictory on " + _serviceEndpoint.Uri);
            serviceClient.BroadcastVictory(newMaster);
            _serviceEndpoint.Logger.LogMessage("Succeeded calling BroadCastVictory on " + _serviceEndpoint.Uri);
        }
    }

    /// <summary>
    /// Class for calling IsMaster on a service. The method does not accept any parameters. The result of the 
    /// service call can be read from the property IsMaster.
    /// </summary>
    internal class IsMasterServiceCaller : ServiceCaller
    {
        public bool IsMaster { get; private set; }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="logger"></param>
        /// <param name="NewMasterId"></param>
        public IsMasterServiceCaller(ServiceEndpoint serviceEnpoint)
            : base(serviceEnpoint)
        {
            IsMaster = false;
        }

        /// <summary>
        /// Called by the Execute method in the base class.
        /// </summary>
        /// <param name="serviceClient"></param>
        protected override void PerformOperation(IMasterElectionService serviceClient)
        {
            _serviceEndpoint.Logger.LogMessage("Calling IsMaster on " + _serviceEndpoint.Uri);
            IsMaster = serviceClient.IsMaster();
            _serviceEndpoint.Logger.LogMessage("Succeeded Calling IsMaster on " + _serviceEndpoint.Uri);
        }
    }


    /// <summary>
    /// Class for calling the method BecomeMaster on a service. The service method has no parametes
    /// and no result object.
    /// </summary>
    internal class BecomeMasterServiceCaller : ServiceCaller
    {
  
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="logger"></param>
        /// <param name="NewMasterId"></param>
        public BecomeMasterServiceCaller(ServiceEndpoint serviceEnpoint)
            : base(serviceEnpoint)
        {
        }

        /// <summary>
        /// Called by the Execute method in the base class.
        /// </summary>
        /// <param name="serviceClient"></param>
        protected override void PerformOperation(IMasterElectionService serviceClient)
        {
            _serviceEndpoint.Logger.LogMessage("Calling BecomeMaster on " + _serviceEndpoint.Uri);
            serviceClient.BecomeMaster();
            _serviceEndpoint.Logger.LogMessage("Succeeded Calling BecomeMaster on " + _serviceEndpoint.Uri);
        }
    }

    /// <summary>
    /// Class for calling RequestMasterElectionId method on a service. The result of the 
    /// servicecall can be requested through the propert RequestMasterElectionIdResult
    /// </summary>
    internal class RequestMasterElectionIdServiceCaller : ServiceCaller
    {
        public RequestMasterElectionIdResult RequestMasterElectionIdResult { get; private set; }
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="uri"></param>
        /// <param name="logger"></param>
        /// <param name="NewMasterId"></param>
        public RequestMasterElectionIdServiceCaller(ServiceEndpoint serviceEnpoint)
            : base(serviceEnpoint)
        {
        }

        /// <summary>
        /// Called by the Execute method in the base class.
        /// </summary>
        /// <param name="serviceClient"></param>
        protected override void PerformOperation(IMasterElectionService serviceClient)
        {
            _serviceEndpoint.Logger.LogMessage("Calling RequestMasterElectionId on " + _serviceEndpoint.Uri);
            RequestMasterElectionIdResult = serviceClient.RequestMasterElectionId();
            _serviceEndpoint.Logger.LogMessage("Succeeded Calling RequestMasterElectionId on " + _serviceEndpoint.Uri);
        }
    }

    public class ServiceEndpoint
    {
        #region Fields
        private readonly ILog _logger;
        readonly string _uri;
        #endregion

        #region Properties

        /// <summary>
        /// Gets the URI of the service this client will be calling
        /// </summary>
        public string Uri
        {
            get { return _uri; }
        }

        public ILog Logger
        {
            get { return _logger; }
        }
        #endregion

        public ServiceEndpoint(string uri, ILog logger)
        {
            _uri = uri;
            _logger = logger;
        }
    }

    /// <summary>
    /// Client of the MasterSlaveElectionService
    /// </summary>
    //public class MasterElectionServiceClient : IMasterElectionService
    //{
    //    #region Fields
    //    private readonly ILog _logger;
    //    readonly string _uri;
    //    #endregion

    //    #region Properties

    //    /// <summary>
    //    /// Gets the URI of the service this client will be calling
    //    /// </summary>
    //    public string Uri
    //    {
    //        get { return _uri; }
    //    }
    //    #endregion

    //    public MasterElectionServiceClient(string uri, ILog logger)
    //    {
    //        _uri = uri;
    //        _logger = logger;
    //    }

    //    #region IMasterElectionService Members

    //    public void BroadcastVictory(string newMaster)
    //    {
    //        IMasterElectionService masterElectionClient = null;
    //        try
    //        {
    //            masterElectionClient = ChannelFactoryHelper.CreateServiceManagementChannel(_logger, this.Uri);

    //            if (masterElectionClient == null)
    //            {
    //                return;
    //            }

    //            using (OperationContextScope scope = new OperationContextScope((IContextChannel)masterElectionClient))
    //            {
    //                _logger.LogMessage("Calling BroadcastVictory on: " + this.Uri);
    //                using (masterElectionClient as IDisposable)
    //                {
    //                    masterElectionClient.BroadcastVictory(newMaster);
    //                }
    //                _logger.LogMessage("Called BroadcastVictory on: " + this.Uri);
    //            }
    //        }
    //        catch (CommunicationException ex)
    //        {
    //            _logger.LogMessage(ex, "Error communicating with server: " + this.Uri);

    //        }
    //        catch (TimeoutException ex)
    //        {
    //            _logger.LogMessage(ex, "Timeout with server: " + this.Uri);
    //        }
    //        catch (Exception ex)
    //        {
    //            _logger.LogMessage(ex, "General exception in communication with server: " + this.Uri);
    //        }

    //    }

    //    public bool IsMaster()
    //    {
    //        bool result = false;
    //        IMasterElectionService masterElectionClient = null;
    //        try
    //        {
    //            masterElectionClient = ChannelFactoryHelper.CreateServiceManagementChannel(_logger, this.Uri);

    //            if (masterElectionClient == null)
    //            {
    //                return result;
    //            }

    //            using (OperationContextScope scope = new OperationContextScope((IContextChannel)masterElectionClient))
    //            {
    //                _logger.LogMessage("Calling IsMaster on: " + this.Uri);
    //                using (masterElectionClient as IDisposable)
    //                {
    //                    result = masterElectionClient.IsMaster();
    //                }
    //                _logger.LogMessage("Called IsMaster on: " + this.Uri);
    //            }
    //        }
    //        catch (CommunicationException ex)
    //        {
    //            _logger.LogMessage(ex, "Error communicating with server: " + this.Uri);
    //        }
    //        catch (TimeoutException ex)
    //        {
    //            _logger.LogMessage(ex, "Timeout with server: " + this.Uri);
    //        }
    //        catch (Exception ex)
    //        {
    //            _logger.LogMessage(ex, "General exception in communication with server: " + this.Uri);
    //        }


    //        return result;
    //    }

    //    public void BecomeMaster()
    //    {
    //        IMasterElectionService masterElectionClient = null;
    //        try
    //        {
    //            masterElectionClient = ChannelFactoryHelper.CreateServiceManagementChannel(_logger, this.Uri);

    //            if (masterElectionClient == null)
    //            {
    //                return;
    //            }

    //            using (OperationContextScope scope = new OperationContextScope((IContextChannel)masterElectionClient))
    //            {
    //                _logger.LogMessage("Calling BecomeMaster on: " + this.Uri);
    //                using (masterElectionClient as IDisposable)
    //                {
    //                    masterElectionClient.BecomeMaster();
    //                }
    //                _logger.LogMessage("Called BecomeMaster on: " + this.Uri);
    //            }
    //        }
    //        catch (CommunicationException ex)
    //        {
    //            _logger.LogMessage(ex, "Error communicating with server: " + this.Uri);

    //        }
    //        catch (TimeoutException ex)
    //        {
    //            _logger.LogMessage(ex, "Timeout with server: " + this.Uri);
    //        }
    //        catch (Exception ex)
    //        {
    //            _logger.LogMessage(ex, "General exception in communication with server: " + this.Uri);
    //        }

    //    }


    //    public RequestMasterElectionIdResult RequestMasterElectionId()
    //    {
    //        RequestMasterElectionIdResult result = null;
    //        IMasterElectionService masterElectionClient = null;
    //        try
    //        {
    //            masterElectionClient = ChannelFactoryHelper.CreateServiceManagementChannel(_logger, this.Uri);

    //            if (masterElectionClient == null)
    //            {
    //                return result;
    //            }

    //            using (OperationContextScope scope = new OperationContextScope((IContextChannel)masterElectionClient))
    //            {
    //                _logger.LogMessage("Calling RequestMasterElectionId on: " + this.Uri);
    //                using (masterElectionClient as IDisposable)
    //                {
    //                    result = masterElectionClient.RequestMasterElectionId();
    //                }
    //                _logger.LogMessage("Called RequestMasterElectionId on: " + this.Uri);
    //            }

    //        }
    //        catch (CommunicationException ex)
    //        {
    //            _logger.LogMessage(ex, "Error communicating with server: " + this.Uri);

    //        }
    //        catch (TimeoutException ex)
    //        {
    //            _logger.LogMessage(ex, "Timeout with server: " + this.Uri);
    //        }
    //        catch (Exception ex)
    //        {
    //            _logger.LogMessage(ex, "General exception in communication with server: " + this.Uri);
    //        }

    //        return result;
    //    }

    //    #endregion

    //}
}
